package com.wyk.session;

import com.wyk.handler.*;

import java.beans.Introspector;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: wykExecutor
 * @Description: todo sql语句执行器实体类
 * @Author: 初久
 * @Date: 2022/7/13 13:53
 **/
public class wykExecutor implements Executor {

    private static Map<Class, TypeHandler> typeHandlerMap = new HashMap<>();

    private wykConfiguration configuration = new wykConfiguration();

    private Connection connection;

    static {
        // todo 不同类型对应的不同的handler
        typeHandlerMap.put(String.class, new StringTypeHandler());
        typeHandlerMap.put(Integer.class, new IntegerTypeHandler());
    }

    // 查询方法
    @Override
    /**
     * @Description //TODO
     * @Param
     * [sql：sql语句,
     * tokenHandler：sql语句中需要替换的？的值对应的名称集合。如:id,
     * paramValuesMap:方法中接受的参数名称的集合
     * clazz:被代理对象的Class模板类
     * ]
     * @return T
     * @Date 10:54 2022/7/14
     **/ public <T> T query(String sql, ParameterMappingTokenHandler tokenHandler, Map<String, Object> paramValuesMap, Method method) {

        PreparedStatement preparedStatement = null;

        ResultSet resultSet = null;

        try {
            // todo 建立连接，获取并完善prepareStatement
            preparedStatement = perfectPreparedStatementForQuery(sql, tokenHandler, paramValuesMap);

            // todo 执行并获取结果
            resultSet = preparedStatement.executeQuery();
            if (resultSet == null) return null;

            // todo 判断并获取Class类型
            Class<T> returnType = drudgeReturnType(method);
            if (returnType == null) {
                throw new RuntimeException("返回值类型未找到");
            }

            /*
             * 0 = "id"
             * 1 = "name"
             * 2 = "age"
             **/
            // todo 获取所有的set方法，并创建方法和名称的映射关系
            Map<String, Method> setMethodMapping = marchAllSetMethods(returnType);

            // todo 将结果集的所有的字段名放入集合中
            List<String> columnList = putAllColumnNames(resultSet);


            // todo 结果处理
            List<Object> result = handleResult(resultSet, returnType, columnList, setMethodMapping);

            // todo 返回结果
//            if (method.getReturnType().equals(List.class)) {
//                return (T) result;
//            } else {
//                return (T) result.get(0);
//            }
            return (T) result;
        } catch (SQLException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            throw new RuntimeException("数据库【query】操作失败\n：" + e);
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    /**
     * @return int 返回值为操作变动的数据个数
     * @Description //TODO 执行增删改操作
     * @Param [java.lang.String, com.wyk.handler.ParameterMappingTokenHandler, java.util.Map<java.lang.String,java.lang.Object>, java.lang.reflect.Method]
     * @Date 15:38 2022/7/16
     **/
    @Override
    public int update(String sql, ParameterMappingTokenHandler tokenHandler, Map<String, Object> paramValuesMap, Method method) {

        PreparedStatement preparedStatement = null;

        try {
            // todo 建立连接，获取并完善prepareStatement
            preparedStatement = perfectPreparedStatementForQuery(sql, tokenHandler, paramValuesMap);

            return preparedStatement.executeUpdate();

        } catch (SQLException e) {
            throw new RuntimeException("数据库【update】操作失败\n:" + e);
        } finally {
            try {
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }

    }

    /**
     * @return java.sql.PreparedStatement
     * @Description //TODO 建立连接，获取并完善prepareStatement
     * @Param [java.lang.String, com.wyk.handler.ParameterMappingTokenHandler, java.util.Map<java.lang.String,java.lang.Object>]
     * @Date 14:03 2022/7/16
     **/
    private PreparedStatement perfectPreparedStatementForQuery(String sql, ParameterMappingTokenHandler tokenHandler, Map<String, Object> paramValuesMap) throws SQLException {
        connection = getConnection();

        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();

        for (int i = 0; i < parameterMappings.size(); i++) {
            // todo 根据parameterMapping的值，通过paramValuesMap映射找到对应的赋值
            String property = parameterMappings.get(i).getProperty();
            Object value = paramValuesMap.get(property);
            // todo 根据这个赋值的类型，通过映射，自动找到对应的嵌入方式，然后插入具体参数
            typeHandlerMap.get(value.getClass()).setParameter(preparedStatement, i + 1, value);
        }

        return preparedStatement;
    }

    private <T> List<Object> handleResult(ResultSet resultSet, Class<T> returnType, List<String> columnList, Map<String, Method> setMethodMapping) throws SQLException, InstantiationException, IllegalAccessException, InvocationTargetException {
        List<Object> result = new ArrayList<>();
        while (resultSet.next()) {
            T t = returnType.newInstance();

            for (String colum : columnList) {
                // todo 根据列名找到set方法映射中对应的set方法，然后赋值
                Method setMethod = setMethodMapping.get(colum);
                // todo 根据参数的类型，执行对应的类型的resultSet查询
                TypeHandler typeHandler = typeHandlerMap.get(setMethod.getParameterTypes()[0]);
                setMethod.invoke(t, typeHandler.getParameter(resultSet, colum));
            }

            result.add(t);
        }
        return result;
    }

    private List<String> putAllColumnNames(ResultSet resultSet) throws SQLException {
        List<String> columnList = new ArrayList<>();
        ResultSetMetaData metaData = resultSet.getMetaData();
        for (int i = 0; i < metaData.getColumnCount(); i++) {
            columnList.add(metaData.getColumnName(i + 1));
        }
        return columnList;
    }

    /**
     * @return void
     * @Description //TODO 获取所有的set方法，并创建方法和名称的映射关系
     * @Param [java.util.Map<java.lang.String, java.lang.reflect.Method>, java.lang.Class<T>]
     * @Date 13:56 2022/7/16
     **/
    private <T> Map<String, Method> marchAllSetMethods(Class<T> returnType) {
        Map<String, Method> setMethodMapping = new HashMap<>();
        for (Method declaredMethod : returnType.getDeclaredMethods()) {
            String methodName = declaredMethod.getName();
            if (methodName.startsWith("set")) {
                // 将字符串变为小写：name
                String setMethodName = Introspector.decapitalize(methodName.substring(3));
                setMethodMapping.put(setMethodName, declaredMethod);
            }
        }
        return setMethodMapping;
    }

    /**
     * @return java.lang.Class<T>
     * @Description //TODO 处理并返回结果类型
     * @Param [java.lang.reflect.Method]
     * @Date 13:55 2022/7/16
     **/
    private <T> Class<T> drudgeReturnType(Method method) {
        // java.util.List<com.wyk.mytest.dao.User>
        Type genericReturnType = method.getGenericReturnType();

        if (genericReturnType instanceof Class) {
            // 不是泛型
            return (Class<T>) genericReturnType;
        } else if (genericReturnType instanceof ParameterizedType) {
            // 是泛型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            return (Class<T>) actualTypeArguments[0];
        }
        return null;
    }


    // todo 获取连接
    private Connection getConnection() {
        try {
            //todo 这个配置文件路径后面应该写到yaml里去
            return configuration.loadXml("wykConfig.xml");
        } catch (Exception e) {
            throw new RuntimeException("获取连接失败");
        }
    }

}
